JavaScriptã®éåæã€ãã¬ãŒã¿ãã«ããŒåæã«ãããéåæããŒã¿åŠçã®èœåãè§£æŸããŸããéåæã¹ããªãŒã æäœãé£çµããå¹ççã§æŽç·Žãããã³ãŒããäœæããæ¹æ³ãåŠã³ãŸãããã
JavaScriptã®éåæã€ãã¬ãŒã¿ãã«ããŒã®åæïŒéåæã¹ããªãŒã ã®ãã§ã€ãã³ã°
éåæããã°ã©ãã³ã°ã¯ãç¹ã«I/Oæäœããããã¯ãŒã¯ãªã¯ãšã¹ãããªã¢ã«ã¿ã€ã ããŒã¿ã¹ããªãŒã ãæ±ãçŸä»£ã®JavaScriptéçºã®åºç€ã§ããECMAScript 2018ã§å°å ¥ãããéåæã€ãã¬ãŒã¿ãšéåæã€ãã©ãã«ã¯ãéåæããŒã¿ã·ãŒã±ã³ã¹ãåŠçããããã®åŒ·åãªã¡ã«ããºã ãæäŸããŸãããã®èšäºã§ã¯ãéåæã€ãã¬ãŒã¿ãã«ããŒã®åæãšããæŠå¿µãæãäžããéåæã¹ããªãŒã ã«å¯Ÿããæäœãé£çµããŠãããã¯ãªãŒã³ã§å¹ççããã€ä¿å®æ§ã®é«ãã³ãŒããå®çŸããæ¹æ³ã解説ããŸãã
éåæã€ãã¬ãŒã¿ãšéåæã€ãã©ãã«ã®çè§£
åæã«ã€ããŠè©³ããèŠãŠããåã«ãåºæ¬çãªããšãæç¢ºã«ããŠãããŸãããïŒ
- éåæã€ãã©ãã« (Async Iterable): `Symbol.asyncIterator`ã¡ãœãããæã€ãªããžã§ã¯ãã§ããã®ã¡ãœããã¯éåæã€ãã¬ãŒã¿ãè¿ããŸããéåæçã«å埩åŠçã§ããããŒã¿ã·ãŒã±ã³ã¹ã衚ããŸãã
- éåæã€ãã¬ãŒã¿ (Async Iterator): `next()`ã¡ãœãããå®çŸ©ãããªããžã§ã¯ãã§ããã®ã¡ãœããã¯`value`ïŒã·ãŒã±ã³ã¹ã®æ¬¡ã®é ç®ïŒãš`done`ïŒã·ãŒã±ã³ã¹ãçµäºãããã©ããã瀺ãããŒã«å€ïŒã®2ã€ã®ããããã£ãæã€ãªããžã§ã¯ãã«è§£æ±ºããããããã¹ãè¿ããŸãã
æ¬è³ªçã«ãéåæã€ãã©ãã«ã¯éåæããŒã¿ã®ãœãŒã¹ã§ãããéåæã€ãã¬ãŒã¿ã¯ãã®ããŒã¿ãäžåºŠã«äžã€ãã€ã¢ã¯ã»ã¹ããããã®ã¡ã«ããºã ã§ããå®äžçã®äŸãšããŠãããŒãžåå²ãããAPIãšã³ããã€ã³ãããããŒã¿ãååŸããå ŽåãèããŠã¿ãŸããããåããŒãžã¯éåæã§å©çšå¯èœãªããŒã¿ã®å¡ã衚ããŸãã
以äžã¯ãæ°å€ã®ã·ãŒã±ã³ã¹ãçæããéåæã€ãã©ãã«ã®ç°¡åãªäŸã§ãïŒ
async function* generateNumbers(max) {
for (let i = 0; i <= max; i++) {
await new Promise(resolve => setTimeout(resolve, 50)); // Simulate asynchronous delay
yield i;
}
}
const numberStream = generateNumbers(5);
(async () => {
for await (const number of numberStream) {
console.log(number); // Output: 0, 1, 2, 3, 4, 5 (with delays)
}
})();
ãã®äŸã§ã¯ã`generateNumbers`ã¯éåæã€ãã©ãã«ãäœæããéåæãžã§ãã¬ãŒã¿é¢æ°ã§ãã`for await...of`ã«ãŒãã¯ãã¹ããªãŒã ããéåæçã«ããŒã¿ãæ¶è²»ããŸãã
éåæã€ãã¬ãŒã¿ãã«ããŒåæã®å¿ èŠæ§
éåæã¹ããªãŒã ã«å¯ŸããŠããã£ã«ã¿ãªã³ã°ããããã³ã°ããªãã¥ãŒã¹ãªã©ã®è€æ°ã®æäœãå®è¡ããå¿ èŠããã°ãã°ãããŸããåŸæ¥ã¯ããããå®çŸããããã«ãã¹ããããã«ãŒããè€éãªéåæé¢æ°ãèšè¿°ããŠãããããããŸãããããããããã¯åé·ã§èªã¿ã«ãããä¿å®ãå°é£ãªã³ãŒãã«ã€ãªããå¯èœæ§ããããŸãã
éåæã€ãã¬ãŒã¿ãã«ããŒã®åæã¯ããããšã¬ã¬ã³ãã§é¢æ°çãªã¢ãããŒããæäŸããŸããããã«ãããæäœãé£çµããŠãããŒã¿ã鿬¡çãã€å®£èšçã«åŠçãããã€ãã©ã€ã³ãäœæã§ããŸããããã¯ã³ãŒãã®åå©çšãä¿é²ããå¯èªæ§ãåäžããããã¹ããç°¡çŽ åããŸãã
APIãããŠãŒã¶ãŒãããã£ãŒã«ã®ã¹ããªãŒã ãååŸããã¢ã¯ãã£ããªãŠãŒã¶ãŒããã£ã«ã¿ãªã³ã°ããæçµçã«åœŒãã®ã¡ãŒã«ã¢ãã¬ã¹ãæœåºããå ŽåãèããŠã¿ãŸãããããã«ããŒã®åæããªããã°ãããã¯ãã¹ããããã³ãŒã«ããã¯å°çã«ãªããããŸããã
éåæã€ãã¬ãŒã¿ãã«ããŒã®æ§ç¯
éåæã€ãã¬ãŒã¿ãã«ããŒãšã¯ãéåæã€ãã©ãã«ãå ¥åãšããŠåãåããå ã®ã¹ããªãŒã ã«ç¹å®ã®å€æãæäœãé©çšããæ°ããéåæã€ãã©ãã«ãè¿ã颿°ã§ãããããã®ãã«ããŒã¯åæå¯èœã«èšèšãããŠãããããããé£çµããŠè€éãªããŒã¿åŠçãã€ãã©ã€ã³ãäœæã§ããŸãã
ããã€ãã®äžè¬çãªãã«ããŒé¢æ°ãå®çŸ©ããŠã¿ãŸãããïŒ
1. `map` ãã«ããŒ
`map`ãã«ããŒã¯ãéåæã¹ããªãŒã ã®åèŠçŽ ã«å€æé¢æ°ãé©çšãã倿ãããå€ãyieldããŸãã
async function* map(iterable, transform) {
for await (const item of iterable) {
yield await transform(item);
}
}
äŸ: æ°å€ã®ã¹ããªãŒã ããã®äºä¹ã«å€æããŸãã
async function* generateNumbers(max) {
for (let i = 0; i <= max; i++) {
await new Promise(resolve => setTimeout(resolve, 50));
yield i;
}
}
const numberStream = generateNumbers(5);
const squareStream = map(numberStream, async (number) => number * number);
(async () => {
for await (const square of squareStream) {
console.log(square); // Output: 0, 1, 4, 9, 16, 25 (with delays)
}
})();
2. `filter` ãã«ããŒ
`filter`ãã«ããŒã¯ãè¿°èªé¢æ°ã«åºã¥ããŠéåæã¹ããªãŒã ããèŠçŽ ããã£ã«ã¿ãªã³ã°ããŸãã
async function* filter(iterable, predicate) {
for await (const item of iterable) {
if (await predicate(item)) {
yield item;
}
}
}
äŸ: ã¹ããªãŒã ããå¶æ°ããã£ã«ã¿ãªã³ã°ããŸãã
async function* generateNumbers(max) {
for (let i = 0; i <= max; i++) {
await new Promise(resolve => setTimeout(resolve, 50));
yield i;
}
}
const numberStream = generateNumbers(5);
const evenNumberStream = filter(numberStream, async (number) => number % 2 === 0);
(async () => {
for await (const evenNumber of evenNumberStream) {
console.log(evenNumber); // Output: 0, 2, 4 (with delays)
}
})();
3. `take` ãã«ããŒ
`take`ãã«ããŒã¯ãéåæã¹ããªãŒã ã®å é ããæå®ãããæ°ã®èŠçŽ ãååŸããŸãã
async function* take(iterable, count) {
let i = 0;
for await (const item of iterable) {
if (i >= count) {
return;
}
yield item;
i++;
}
}
äŸ: ã¹ããªãŒã ããæåã®3ã€ã®æ°å€ãååŸããŸãã
async function* generateNumbers(max) {
for (let i = 0; i <= max; i++) {
await new Promise(resolve => setTimeout(resolve, 50));
yield i;
}
}
const numberStream = generateNumbers(5);
const firstThreeNumbers = take(numberStream, 3);
(async () => {
for await (const number of firstThreeNumbers) {
console.log(number); // Output: 0, 1, 2 (with delays)
}
})();
4. `toArray` ãã«ããŒ
`toArray`ãã«ããŒã¯ãéåæã¹ããªãŒã å šäœãæ¶è²»ãããã¹ãŠã®èŠçŽ ãå«ãé åãè¿ããŸãã
async function toArray(iterable) {
const result = [];
for await (const item of iterable) {
result.push(item);
}
return result;
}
äŸ: æ°å€ã®ã¹ããªãŒã ãé åã«å€æããŸãã
async function* generateNumbers(max) {
for (let i = 0; i <= max; i++) {
await new Promise(resolve => setTimeout(resolve, 50));
yield i;
}
}
const numberStream = generateNumbers(5);
(async () => {
const numbersArray = await toArray(numberStream);
console.log(numbersArray); // Output: [0, 1, 2, 3, 4, 5]
})();
5. `flatMap` ãã«ããŒ
`flatMap`ãã«ããŒã¯ãåèŠçŽ ã«é¢æ°ãé©çšãããã®çµæãåäžã®éåæã¹ããªãŒã ã«ãã©ããåããŸãã
async function* flatMap(iterable, transform) {
for await (const item of iterable) {
const transformedIterable = await transform(item);
for await (const transformedItem of transformedIterable) {
yield transformedItem;
}
}
}
äŸ: æååã®ã¹ããªãŒã ãæåã®ã¹ããªãŒã ã«å€æããŸãã
async function* generateStrings() {
await new Promise(resolve => setTimeout(resolve, 50));
yield "hello";
await new Promise(resolve => setTimeout(resolve, 50));
yield "world";
}
const stringStream = generateStrings();
const charStream = flatMap(stringStream, async (str) => {
async function* stringToCharStream() {
for (let i = 0; i < str.length; i++) {
yield str[i];
}
}
return stringToCharStream();
});
(async () => {
for await (const char of charStream) {
console.log(char); // Output: h, e, l, l, o, w, o, r, l, d (with delays)
}
})();
éåæã€ãã¬ãŒã¿ãã«ããŒã®åæ
éåæã€ãã¬ãŒã¿ãã«ããŒã®ç䟡ã¯ããã®åæå¯èœæ§ã«ãããŸããããããé£çµããŠãè€éãªããŒã¿åŠçãã€ãã©ã€ã³ãäœæã§ããŸãããããå æ¬çãªäŸã§ç€ºããŸãããïŒ
ã·ããªãª: ããŒãžåå²ãããAPIãããŠãŒã¶ãŒããŒã¿ãååŸããã¢ã¯ãã£ããªãŠãŒã¶ãŒããã£ã«ã¿ãªã³ã°ãã圌ãã®ã¡ãŒã«ã¢ãã¬ã¹ãæœåºããæåã®5ã€ã®ã¡ãŒã«ã¢ãã¬ã¹ãååŸããŸãã
async function* fetchUsers(apiUrl) {
let page = 1;
while (true) {
const response = await fetch(`${apiUrl}?page=${page}`);
const data = await response.json();
if (data.length === 0) {
return; // No more data
}
for (const user of data) {
yield user;
}
page++;
await new Promise(resolve => setTimeout(resolve, 200)); // Simulate API delay
}
}
// Sample API URL (replace with a real API endpoint)
const apiUrl = "https://example.com/api/users";
const userStream = fetchUsers(apiUrl);
const activeUserEmailStream = take(
map(
filter(
userStream,
async (user) => user.isActive
),
async (user) => user.email
),
5
);
(async () => {
const activeUserEmails = await toArray(activeUserEmailStream);
console.log(activeUserEmails); // Output: Array of the first 5 active user emails
})();
ãã®äŸã§ã¯ã`filter`ã`map`ã`take`ãã«ããŒãé£çµããŠãŠãŒã¶ãŒããŒã¿ã¹ããªãŒã ãåŠçããŸãã`filter`ãã«ããŒã¯ã¢ã¯ãã£ããªãŠãŒã¶ãŒã®ã¿ãéžæãã`map`ãã«ããŒã¯åœŒãã®ã¡ãŒã«ã¢ãã¬ã¹ãæœåºãã`take`ãã«ããŒã¯çµæãæåã®5ã€ã®ã¡ãŒã«ã¢ãã¬ã¹ã«å¶éããŸãããã®ãã¹ãã«æ³šæããŠãã ãããããã¯äžè¬çã§ããã以äžã§èŠãããã«ãŠãŒãã£ãªãã£é¢æ°ã§æ¹åã§ããŸãã
ãã€ãã©ã€ã³ãŠãŒãã£ãªãã£ã«ããå¯èªæ§ã®åäž
äžèšã®äŸã¯åæã瀺ããŠããŸãããããè€éãªãã€ãã©ã€ã³ã§ã¯ãã¹ããæ±ãã«ãããªãå¯èœæ§ããããŸããå¯èªæ§ãåäžãããããã«ã`pipeline`ãŠãŒãã£ãªãã£é¢æ°ãäœæã§ããŸãïŒ
async function pipeline(iterable, ...operations) {
let result = iterable;
for (const operation of operations) {
result = operation(result);
}
return result;
}
ããã§ã`pipeline`颿°ã䜿çšããŠåã®äŸãæžãçŽãããšãã§ããŸãïŒ
async function* fetchUsers(apiUrl) {
let page = 1;
while (true) {
const response = await fetch(`${apiUrl}?page=${page}`);
const data = await response.json();
if (data.length === 0) {
return; // No more data
}
for (const user of data) {
yield user;
}
page++;
await new Promise(resolve => setTimeout(resolve, 200)); // Simulate API delay
}
}
// Sample API URL (replace with a real API endpoint)
const apiUrl = "https://example.com/api/users";
const userStream = fetchUsers(apiUrl);
const activeUserEmailStream = pipeline(
userStream,
(stream) => filter(stream, async (user) => user.isActive),
(stream) => map(stream, async (user) => user.email),
(stream) => take(stream, 5)
);
(async () => {
const activeUserEmails = await toArray(activeUserEmailStream);
console.log(activeUserEmails); // Output: Array of the first 5 active user emails
})();
ãã®ããŒãžã§ã³ã¯ã¯ããã«èªã¿ããããçè§£ããããã§ãã`pipeline`颿°ã¯æäœã鿬¡çã«é©çšããããŒã¿ã®æµããããæç¢ºã«ããŸãã
ãšã©ãŒãã³ããªã³ã°
éåææäœãæ±ãéã«ã¯ããšã©ãŒãã³ããªã³ã°ãéåžžã«éèŠã§ãã`yield`ã¹ããŒãã¡ã³ãã`try...catch`ãããã¯ã§ã©ããããããšã§ããã«ããŒé¢æ°ã«ãšã©ãŒãã³ããªã³ã°ãçµã¿èŸŒãããšãã§ããŸãã
async function* map(iterable, transform) {
for await (const item of iterable) {
try {
yield await transform(item);
} catch (error) {
console.error("Error in map helper:", error);
// You can choose to re-throw the error, skip the item, or yield a default value.
// For example, to skip the item:
// continue;
}
}
}
ã¢ããªã±ãŒã·ã§ã³ã®èŠä»¶ã«åºã¥ããŠããšã©ãŒãé©åã«åŠçããããšãå¿ããªãã§ãã ããããšã©ãŒããã°ã«èšé²ããããåé¡ã®ããé ç®ãã¹ããããããããã€ãã©ã€ã³ãçµäºããããããããšãèããããŸãã
éåæã€ãã¬ãŒã¿ãã«ããŒåæã®å©ç¹
- å¯èªæ§ã®åäž: ã³ãŒãããã宣èšçã§çè§£ãããããªããŸãã
- åå©çšæ§ã®åäž: ãã«ããŒé¢æ°ã¯ã¢ããªã±ãŒã·ã§ã³ã®ããŸããŸãªéšåã§åå©çšã§ããŸãã
- ãã¹ãã®ç°¡çŽ å: ãã«ããŒé¢æ°ã¯åå¥ã«ãã¹ãããã®ã容æã§ãã
- ä¿å®æ§ã®åäž: 1ã€ã®ãã«ããŒé¢æ°ãžã®å€æŽãããã€ãã©ã€ã³ã®ä»ã®éšåã«åœ±é¿ãäžããŸããïŒå ¥åºåã®å¥çŽãç¶æãããŠããéãïŒã
- ããè¯ããšã©ãŒãã³ããªã³ã°: ãšã©ãŒãã³ããªã³ã°ããã«ããŒé¢æ°å ã«éçŽã§ããŸãã
å®äžçã§ã®å¿çš
éåæã€ãã¬ãŒã¿ãã«ããŒã®åæã¯ã以äžã®ãããªããŸããŸãªã·ããªãªã§äŸ¡å€ããããŸãïŒ
- ããŒã¿ã¹ããªãŒãã³ã°: ã»ã³ãµãŒãããã¯ãŒã¯ãéèãã£ãŒãããœãŒã·ã£ã«ã¡ãã£ã¢ã¹ããªãŒã ãªã©ã®ãœãŒã¹ããã®ãªã¢ã«ã¿ã€ã ããŒã¿ã®åŠçã
- APIçµ±å: ããŒãžåå²ãããAPIãè€æ°ã®ããŒã¿ãœãŒã¹ããã®ããŒã¿ã®ååŸãšå€æãäŸãã°ãããŸããŸãªeã³ããŒã¹ãã©ãããã©ãŒã ïŒAmazonãeBayãèªç€Ÿã¹ãã¢ïŒããããŒã¿ãéçŽããŠãçµ±äžãããååãªã¹ããçæããããšãæ³åããŠã¿ãŠãã ããã
- ãã¡ã€ã«åŠç: 倧ããªãã¡ã€ã«ã®éåæçãªèªã¿åããšåŠçãäŸãã°ã倧ããªCSVãã¡ã€ã«ãè§£æããç¹å®ã®åºæºïŒäŸïŒæ¥æ¬ã§ã®å£²äžããããå€ãè¶ ããïŒã«åºã¥ããŠè¡ããã£ã«ã¿ãªã³ã°ããåæã®ããã«ããŒã¿ã倿ãããªã©ã
- ãŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹ã®æŽæ°: ããŒã¿ãå©çšå¯èœã«ãªãã«ã€ããŠUIèŠçŽ ãæ®µéçã«æŽæ°ããŸããäŸãã°ããªã¢ãŒããµãŒããŒããååŸãããæ€çŽ¢çµæããã®ãŸãŸè¡šç€ºããããšã§ããããã¯ãŒã¯æ¥ç¶ãé ãå Žåã§ãã¹ã ãŒãºãªãŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãæäŸããŸãã
- ãµãŒããŒã»ã³ãã€ãã³ã (SSE): SSEã¹ããªãŒã ãåŠçããã¿ã€ãã«åºã¥ããŠã€ãã³ãããã£ã«ã¿ãªã³ã°ãã衚瀺ããããªãåŠçã®ããã«ããŒã¿ã倿ããŸãã
èæ ®äºé ãšãã¹ããã©ã¯ãã£ã¹
- ããã©ãŒãã³ã¹: éåæã€ãã¬ãŒã¿ãã«ããŒã¯ã¯ãªãŒã³ã§ãšã¬ã¬ã³ããªã¢ãããŒããæäŸããŸãããããã©ãŒãã³ã¹ã«ã¯æ³šæããŠãã ãããåãã«ããŒé¢æ°ã¯ãªãŒããŒãããã远å ãããããé床ãªãã§ã€ãã³ã°ã¯é¿ããŠãã ãããç¹å®ã®ã·ããªãªã§ã¯ãåäžã®ããè€éãªé¢æ°ã®æ¹ãå¹ççã§ããå¯èœæ§ãèæ ®ããŠãã ããã
- ã¡ã¢ãªäœ¿çšé: å€§èŠæš¡ãªã¹ããªãŒã ãæ±ãéã«ã¯ãã¡ã¢ãªäœ¿çšéã«æ³šæããŠãã ããã倧éã®ããŒã¿ãã¡ã¢ãªã«ãããã¡ãªã³ã°ããããšã¯é¿ããŠãã ããã`take`ãã«ããŒã¯åŠçããããŒã¿éãå¶éããã®ã«åœ¹ç«ã¡ãŸãã
- ãšã©ãŒãã³ããªã³ã°: äºæãã¬ã¯ã©ãã·ã¥ãããŒã¿ç Žæãé²ãããã«ãå ç¢ãªãšã©ãŒãã³ããªã³ã°ãå®è£ ããŠãã ããã
- ãã¹ã: ãã«ããŒé¢æ°ãæåŸ ã©ããã«åäœããããšã確èªããããã«ãå æ¬çãªåäœãã¹ããèšè¿°ããŠãã ããã
- äžå€æ§: ããŒã¿ã¹ããªãŒã ãäžå€ãªãã®ãšããŠæ±ã£ãŠãã ããããã«ããŒé¢æ°å ã§å ã®ããŒã¿ã倿Žããã®ã§ã¯ãªããæ°ãããªããžã§ã¯ããå€ãäœæããŠãã ããã
- TypeScript: TypeScriptã䜿çšãããšãéåæã€ãã¬ãŒã¿ãã«ããŒã³ãŒãã®åå®å šæ§ãšä¿å®æ§ãå€§å¹ ã«åäžãããããšãã§ããŸããããŒã¿æ§é ã«æç¢ºãªã€ã³ã¿ãŒãã§ãŒã¹ãå®çŸ©ãããžã§ããªã¯ã¹ã䜿çšããŠåå©çšå¯èœãªãã«ããŒé¢æ°ãäœæããŠãã ããã
çµè«
JavaScriptã®éåæã€ãã¬ãŒã¿ãã«ããŒã®åæã¯ãéåæããŒã¿ã¹ããªãŒã ãåŠçããããã®åŒ·åã§ãšã¬ã¬ã³ããªæ¹æ³ãæäŸããŸããæäœãé£çµããããšã§ãã¯ãªãŒã³ã§åå©çšå¯èœããã€ä¿å®æ§ã®é«ãã³ãŒããäœæã§ããŸããæåã®ã»ããã¢ããã¯è€éã«èŠãããããããŸããããå¯èªæ§ããã¹ãæ§ãä¿å®æ§ã®åäžãšããå©ç¹ã¯ãéåæããŒã¿ãæ±ããã¹ãŠã®JavaScriptéçºè ã«ãšã£ãŠäŸ¡å€ã®ããæè³ã§ãã
éåæã€ãã¬ãŒã¿ã®åãæŽ»çšããéåæJavaScriptã³ãŒãã®å¹çæ§ãšãšã¬ã¬ã³ã¹ãæ°ããªã¬ãã«ã«åŒãäžããŸããããããŸããŸãªãã«ããŒé¢æ°ã詊ããŠãããŒã¿åŠçã¯ãŒã¯ãããŒãã©ã®ããã«ç°¡çŽ åã§ããããçºèŠããŠãã ãããããã©ãŒãã³ã¹ãšã¡ã¢ãªäœ¿çšéãèæ ®ããåžžã«å ç¢ãªãšã©ãŒãã³ããªã³ã°ãåªå ããããšãå¿ããªãã§ãã ããã